package domain.grammar;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import domain.regularExpression.NonUnaryRegularExpressionOperator;
import domain.regularExpression.RegularExpressionElement;
import domain.regularExpression.UnaryRegularExpressionOperator;

public class ExtGrammarCalculator {
	private List<RegularExpressionElement> _nodes;
	private List<Boolean> _flag;
	private List<Set<ExtendedTerminalSymbol>> _calcList;
	private ExtendedGrammar _extGrammar;
	
	public ExtGrammarCalculator(ExtendedGrammar extGr){
		_extGrammar = extGr;
		_nodes = new Vector<RegularExpressionElement>();
		Iterator<ExtendedProduction> itProd = extGr.get_productions().get_productions().iterator();
		while(itProd.hasNext()){
			ExtendedProduction extProd = itProd.next();
			_nodes.add(extProd.get_leftSide());
			_nodes.addAll(extProd.get_rightSide().getListOfElemOfClass(ExtendedTerminalSymbol.class));
			_nodes.addAll(extProd.get_rightSide().getListOfElemOfClass(UnaryRegularExpressionOperator.class));
			_nodes.addAll(extProd.get_rightSide().getListOfElemOfClass(NonUnaryRegularExpressionOperator.class));
		}
		_flag = new Vector<Boolean>();
		int index=0;
		while(index<_nodes.size()){
			_flag.add(false);
			index++;
		}
		initializeCalcList();
	}
	
	
	private void initializeCalcList(){
		_calcList = new Vector<Set<ExtendedTerminalSymbol>>();
		int index=0;
		while(index<_nodes.size()){
			HashSet<ExtendedTerminalSymbol> aSet = new HashSet<ExtendedTerminalSymbol>();
			_calcList.add(aSet);
			index++;
		}
	}
	
	
	private void uncheckAllNodes(){
		int index = 0;
		while(index<_flag.size()){
			_flag.set(index, false);
			index++;
		}
	}
	
	
	private boolean isAllChecked(){
		int index = 0;
		boolean result = true;
		while(index<_flag.size() && result){
			result = result && _flag.get(index);
			index++;
		}
		return result;
	}
	
	private boolean isAllUnchecked(){
		int index = 0;
		boolean result = true;
		while(index<_flag.size() && result){
			result = result && !(_flag.get(index));
			index++;
		}
		return result;
	}
	
	
	public Map calculateNexts(
			Map<RegularExpressionElement,Boolean> nullDicc, 
			Map<RegularExpressionElement,Set<ExtendedTerminalSymbol>> firstsDicc){
		initializeCalcList();
		addEndOfStringToMainSymb();
		makeNextsIteration(nullDicc,firstsDicc);
		while(!isAllUnchecked()){
			makeNextsIteration(nullDicc,firstsDicc);
		}
		return makeMapWithCalculated();
	}
	
	private Map makeMapWithCalculated(){
		Map result = new HashMap<RegularExpressionElement, Set<ExtendedTerminalSymbol>>();
		int index = 0;
		while(index < _nodes.size()){
			result.put(_nodes.get(index), _calcList.get(index));
			index++;
		}
		return result;
	}
	
	private void addEndOfStringToMainSymb(){
		ExtendedNonTerminalSymbol extNT = this._extGrammar.get_mainSymbol();
		int indexMainSymb = _nodes.indexOf(extNT);
		this._calcList.get(indexMainSymb).add(ExtendedTerminalSymbol.endOfString());
	}
	
	
	private void makeNextsIteration(
			Map<RegularExpressionElement,Boolean> nullDicc,
			Map<RegularExpressionElement,Set<ExtendedTerminalSymbol>> firstsDicc){
		uncheckAllNodes();
		int index = 0;
		while(index < _nodes.size()){
			calculateNextsForNode(nullDicc,firstsDicc,index);
			index++;
		}
	}
	
	private void calculateNextsForNode(
			Map<RegularExpressionElement,Boolean> nullDicc, 
			Map<RegularExpressionElement,Set<ExtendedTerminalSymbol>> firstsDicc,
			int index){
		RegularExpressionElement aNode = _nodes.get(index);
		if(aNode instanceof ExtendedNonTerminalSymbol){
			passNextsToProdRigthSide((ExtendedNonTerminalSymbol)aNode,_calcList.get(index));
		}else if(aNode instanceof NonUnaryRegularExpressionOperator){
			NonUnaryRegularExpressionOperator nonUnaryOp = (NonUnaryRegularExpressionOperator) aNode;
			if(nonUnaryOp.isConcatenation()){
				calculateNextsInConcatenationNode(nullDicc,firstsDicc,nonUnaryOp,index);
			}else if(nonUnaryOp.isPipe()){
				passNextToAllSubElements(nonUnaryOp,_calcList.get(index));
			}
		}else if(aNode instanceof UnaryRegularExpressionOperator){
			passNextToAllSubElements(aNode,_calcList.get(index));
		}
	}


	private void calculateNextsInConcatenationNode( 
			Map<RegularExpressionElement,Boolean> nullDicc,
			Map<RegularExpressionElement,Set<ExtendedTerminalSymbol>> firstsDicc,
			NonUnaryRegularExpressionOperator nonUnaryOp, int concatIndex) {

		List<RegularExpressionElement> subElems = nonUnaryOp.getSubElements();
		RegularExpressionElement lastElem = subElems.get(subElems.size() - 1);
		Set<ExtendedTerminalSymbol> concatNext = _calcList.get(concatIndex);
		boolean elemsAdded = _calcList.get(_nodes.indexOf(lastElem)).addAll(concatNext);
		if(elemsAdded){
			_flag.set(_nodes.indexOf(lastElem), true);
		}
		
		int index1 = 0;
		while(index1 < subElems.size()){
			int index2 = index1 + 1;
			while(index2 < subElems.size()){
				RegularExpressionElement fwdElem = subElems.get(index2);
				Set<ExtendedTerminalSymbol> fwdFirsts = firstsDicc.get(fwdElem);
				
				elemsAdded = _calcList.get(_nodes.indexOf(subElems.get(index1))).addAll(fwdFirsts);
				if(elemsAdded){
					_flag.set(_nodes.indexOf(subElems.get(index1)), true);
				}
				if(!nullDicc.get(fwdElem)){
					index2 = subElems.size();
				}else{
					index2++;
				}
			}
			if((index1+1 < (subElems.size())) && allNulables(nullDicc,subElems.subList(index1+1, subElems.size()))){
				elemsAdded = _calcList.get(_nodes.indexOf(subElems.get(index1))).addAll(concatNext);
				if(elemsAdded){
					_flag.set(_nodes.indexOf(subElems.get(index1)), true);
				}
			}
			index1 ++;
		}
		
	}
	
	private boolean allNulables(Map<RegularExpressionElement,Boolean> nullDicc, List<RegularExpressionElement> reeLst){
		Iterator<RegularExpressionElement> itReeLst = reeLst.iterator();
		boolean result = true;
		while(itReeLst.hasNext()){
			RegularExpressionElement aRee = itReeLst.next();
			result = result && nullDicc.get(aRee);
		}
		return result;
	}


	private void passNextToAllSubElements(
			RegularExpressionElement ree,
			Set<ExtendedTerminalSymbol> fatherNext) {
		Iterator<RegularExpressionElement> itSubElem = ree.getSubElements().iterator();
		while(itSubElem.hasNext()){
			RegularExpressionElement aSubElem = itSubElem.next();
			int subElemIndx = _nodes.indexOf(aSubElem);
			boolean elemsAdded = _calcList.get(subElemIndx).addAll(fatherNext);
			if(elemsAdded){
				_flag.set(subElemIndx, true);
			}
		}
		
	}


	private void passNextsToProdRigthSide(ExtendedNonTerminalSymbol node,
			Set<ExtendedTerminalSymbol> set) {
		RegularExpressionElement rightElem = _extGrammar.get_productions().getProductionByLeftSide(node)
															.get_rightSide().getRootElement();
		int rightElemIndx = _nodes.indexOf(rightElem);
		boolean elemsAdded = _calcList.get(rightElemIndx).addAll(set);
		if(elemsAdded){
			_flag.set(rightElemIndx, true);
		}
	}
	
	
	
	public Map calculateFirsts(Map<RegularExpressionElement,Boolean> nullDicc){
		initializeCalcList();
		makeFirstsIteration(nullDicc);
		while(!isAllUnchecked()){
			makeFirstsIteration(nullDicc);
		}
		return makeMapWithCalculated();
	}
	
	
	private void makeFirstsIteration(Map<RegularExpressionElement,Boolean> nullDicc){
		uncheckAllNodes();
		int index = 0;
		while(index < _nodes.size()){
			calculateFirstsForNode(nullDicc, index);
			index++;
		}
	}


	private void calculateFirstsForNode(Map<RegularExpressionElement,Boolean> nullDicc, int index) {
		RegularExpressionElement aNode = _nodes.get(index);
		if(aNode instanceof ExtendedTerminalSymbol){
			Set<ExtendedTerminalSymbol> aTerm= new HashSet<ExtendedTerminalSymbol>();
			aTerm.add((ExtendedTerminalSymbol)aNode);
			addToCalcList(index,aTerm);
		}else if(aNode instanceof ExtendedNonTerminalSymbol){
			ExtendedNonTerminalSymbol extNT = (ExtendedNonTerminalSymbol)aNode;
			RegularExpressionElement child = 
				_extGrammar.get_productions().getProductionByLeftSide(extNT).get_rightSide().getRootElement();
			addToCalcList(index,_calcList.get(_nodes.indexOf(child)));
			
		}else if(aNode instanceof NonUnaryRegularExpressionOperator){
			NonUnaryRegularExpressionOperator nonUnaryOp = (NonUnaryRegularExpressionOperator) aNode;
			if(nonUnaryOp.isConcatenation()){
				calculateFirstsInConcatenationNode(nonUnaryOp,index,nullDicc);
			}else if(nonUnaryOp.isPipe()){
				Iterator<RegularExpressionElement> itSubElems = nonUnaryOp.getSubElements().iterator();
				while(itSubElems.hasNext()){
					RegularExpressionElement aChild = itSubElems.next();
					addToCalcList(index,_calcList.get(_nodes.indexOf(aChild)));
				}
			}
		}else if(aNode instanceof UnaryRegularExpressionOperator){
			UnaryRegularExpressionOperator unaryOp = (UnaryRegularExpressionOperator) aNode;
			RegularExpressionElement child = unaryOp.getSubElements().get(0);
			addToCalcList(index,_calcList.get(_nodes.indexOf(child)));
		}
	}
	
	
	
	
	private void calculateFirstsInConcatenationNode(
			NonUnaryRegularExpressionOperator nonUnaryOp, int index, Map<RegularExpressionElement,Boolean> nullDicc) {
		List<RegularExpressionElement> subElems = nonUnaryOp.getSubElements();
		int indexOfFirstNotNullable = 0;
		while(indexOfFirstNotNullable<subElems.size() && nullDicc.get(subElems.get(indexOfFirstNotNullable))){
			indexOfFirstNotNullable++;
		}
		
		int i = 0;
		while(i<indexOfFirstNotNullable){
			addToCalcList(index,_calcList.get(_nodes.indexOf(subElems.get(i))));
			i++;
		}
		if(indexOfFirstNotNullable<subElems.size()){
			addToCalcList(index,_calcList.get(_nodes.indexOf(subElems.get(indexOfFirstNotNullable))));
		}
	}


	private void addToCalcList(int calListIndx, Set<ExtendedTerminalSymbol> setToAdd){
		boolean elemsAdded = _calcList.get(calListIndx).addAll(setToAdd);
		if(elemsAdded){
			_flag.set(calListIndx, true);
		}
	}
	

}
